package cn._2dland.websocket.io;

import java.io.IOException;
import java.io.OutputStream;

import cn._2dland.websocket.model.message.BinaryMessage;
import cn._2dland.websocket.model.message.ControlMessage;
import cn._2dland.websocket.model.message.Message;
import cn._2dland.websocket.model.message.MessageTypesV08;
import cn._2dland.websocket.model.message.TextMessage;

/**
 * for hybi ver. 8.0~17.0
 *  
 * @author deadblue
 */
public class MessageOutputStreamV08 extends MessageOutputStream {

	public MessageOutputStreamV08(OutputStream out) {
		super(out);
	}

	@Override
	public void writeMessage(Message msg) throws IOException {
		// 分类型写入
		if(msg instanceof ControlMessage) {
			writeControlMessage((ControlMessage) msg);
		} else if(msg instanceof BinaryMessage) {
			writeBinaryMessage((BinaryMessage) msg);
		} else if(msg instanceof TextMessage) {
			writeTextMessage((TextMessage) msg);
		}
	}

	/**
	 * 写入控制报文
	 * 
	 * @param msg
	 * @throws IOException
	 */
	private void writeControlMessage(ControlMessage msg) throws IOException {
		// 获取消息类型
		byte type = 0;
		switch(msg.getCommand()) {
		case ControlMessage.CLOSE:
			type = MessageTypesV08.TYPE_CTRL_CLOSE;
			break;
		case ControlMessage.PING:
			type = MessageTypesV08.TYPE_CTRL_PING;
			break;
		case ControlMessage.PONG:
			type = MessageTypesV08.TYPE_CTRL_PONG;
			break;
		}

		writeRawMessage(type, null);
	}

	/**
	 * 写入二进制报文
	 * 
	 * @param msg
	 * @throws IOException
	 */
	private void writeBinaryMessage(BinaryMessage msg) throws IOException {
		writeRawMessage(MessageTypesV08.TYPE_DATA_BINARY, msg.getBinary());
	}

	/**
	 * 写入文本报文
	 * 
	 * @param msg
	 * @throws IOException
	 */
	private void writeTextMessage(TextMessage msg) throws IOException {
		byte[] data = msg.getText().getBytes("utf-8");
		writeRawMessage(MessageTypesV08.TYPE_DATA_TEXT, data);
	}

	/**
	 * 统一写原生消息的方法
	 * 异常往上抛～
	 * 
	 * @param type 报文类型
	 * @param data 数据
	 * @throws IOException
	 */
	private void writeRawMessage(byte type, byte[] data) throws IOException {
		// 不合法的报文类型，忽略
		if(type == MessageTypesV08.TYPE_UNKNOWN)
			return;

		// 写入消息头
		// TODO: 这里直接一次性写完报文，未来要考虑分帧
		int msgHead = 0x80 | (type & 0x0f);
		out.write(msgHead);

		// 数据长度
		int len = data == null ? 0 : data.length;

		// 数据头
		// TODO: 这里为求简单，没使用掩码，未来要加上
		int dataHead = 0;
		// 存储长度用的字节数
		int lenByteCount = 0;
		if(len <= 125) {
			dataHead |= len & 0x7f;
		} else if(len <= 0xffff) {
			dataHead |= 0x7e;
			lenByteCount = 2;
		} else {
			dataHead |= 0x7f;
			lenByteCount = 8;
		}
		// 写入数据头
		out.write(dataHead);
		// 写入数据长度
		for(int i = lenByteCount - 1; i >= 0; i--) {
			int b = len >> (i * 8);
			out.write(b);
		}

		// 写入数据（暂时不做掩码处理）
		if(data != null) {
			out.write(data);
		}
	}
}